package cn.xiaosiyuan.javaagenthelloworld;

import javassist.*;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

@SpringBootApplication
public class JavaagenthelloworldApplication {

    final static String source = "{\n" +
            "        long begin = System.currentTimeMillis();\n" +
            "        Object result;\n" +
            "        try {\n" +
            "            result = ($w)%s$agent($$);\n" +
            "        } finally {\n" +
            "            long end = System.currentTimeMillis();\n" +
            "            System.out.println(end - begin);\n" +
            "        }\n" +
            "        return ($r) result;\n" +
            "    }";

    final static String voidSource = "{\n" +
            "        long begin = System.currentTimeMillis();\n" +
            "        try {\n" +
            "            %s$agent($$);\n" +
            "        } finally {\n" +
            "            long end = System.currentTimeMillis();\n" +
            "            System.out.println(end - begin);\n" +
            "        }\n" +
            "    }";

//    public static void premain (String agentOps,Instrumentation Instrumentation){
//        System.out.println("====premain 方法执行");
//
//        final ClassPool pool = new ClassPool();
//        pool.appendSystemPath();
//
////        添加类装载过滤器
//        Instrumentation.addTransformer(new ClassFileTransformer() {
//            /*
//            这里监控的是cn/xiaosiyuan/javaagenthelloworld/UserService类中的sayHello方法
//            * */
//            @Override
//            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//                if (!"cn/xiaosiyuan/javaagenthelloworld/UserService".equals(className)){
//                    return null;
//                }
//                try {
//                    CtClass ctClass = pool.get("cn.xiaosiyuan.javaagenthelloworld.UserService");
//                    CtMethod sayHello = ctClass.getDeclaredMethod("sayHello");
//                    sayHello.insertBefore("System.out.println(System.currentTimeMillis());");
//
////                    1.复制方法
//                    CtMethod copy = CtNewMethod.copy(sayHello, ctClass, null);
//                    copy.setName("sayHello$agent");
//                    ctClass.addMethod(copy);
////                    2.改变原有方法
//                    sayHello.setBody("{ long begin = System.currentTimeMillis();\n" +
//                            "                    sayHello$agent($1);\n" +
//                            "                    System.out.println(System.currentTimeMillis() - begin); }");
//                    return ctClass.toBytecode();
//                } catch (NotFoundException e) {
//                    e.printStackTrace();
//                } catch (CannotCompileException e) {
//                    e.printStackTrace();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//                return null;
//            }
//        });
//
//
//
//
////        打印所有的IDEA中类
////        idea→Help→edit custom vm options→（这时候自动打开了 idea64.exe.vmoptions）→
////        在-javaagent:C:\Users\Public\.jetbrains\jetbrains-agent-v3.2.1.c46b.ed7=by https://zhile.io
////        后面加上一行代码
////        -javaagent:D:\personal\project\javaagenthelloworld\out\artifacts\javaagenthelloworld_jar\javaagenthelloworld.jar
////        即jar包绝对路径
////        notes：如果文件的末尾没有一个空行，就会报错。
//
//        /*
//C:\Users\xiaosiyuan\AppData\Roaming\JetBrains\IntelliJIdea2020.1\idea64.exe.vmoptions
//千万别改，改了直接启动不了idea，视频教程里面的苹果电脑启动了，我的直接打不开
//        * */
//
////        启动idea的时候运行
////        电脑不好，可能会卡崩
////        Instrumentation.addTransformer(new ClassFileTransformer() {
////            @Override
////            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
////                System.out.println(className);
////                return null;
////            }
////        });
//
//    }

//    不写一个空的main，maven在install的时候会报错
    public static void main(String[] args) {

    }

    // 通过设置监控指定区域的方法和类

// cn.xiaosiyuan.javaagenthelloworld.*

// 读取参数配置

// 匹配类

// 获取这个类下的所有方法

    // 改造这个方法
    public static void premain(String arg,Instrumentation instrumentation) {
        final String config = arg;
        final ClassPool pool = new ClassPool();
        pool.appendSystemPath();
        instrumentation.addTransformer(new ClassFileTransformer() {
            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                if(className == null || !className.replaceAll("/",".").startsWith(config)){
                    return null;
                }
                try {
                    className = className.replaceAll("/",".");
                    CtClass ctClass = pool.get(className);
//                获取它的所有方法
                    for (CtMethod declaredMethod:ctClass.getDeclaredMethods()) {
                        newMethod(declaredMethod);
                    }
                    return ctClass.toBytecode();
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    public static CtMethod newMethod(CtMethod oldMethod) throws CannotCompileException, NotFoundException {
        CtMethod copy = CtNewMethod.copy(oldMethod, oldMethod.getDeclaringClass(), null);
        copy.setName(oldMethod.getName() + "$agent");
        oldMethod.getDeclaringClass().addMethod(copy);

        if (oldMethod.getReturnType().equals(CtClass.voidType)){
            oldMethod.setBody(String.format(voidSource,oldMethod.getName()));
        }else {
            oldMethod.setBody(String.format(source,oldMethod.getName()));
        }
        return copy;
    }
}





